/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.meta.service.dictionary.impl.ext;

import com.alibaba.fastjson2.JSON;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.je.common.base.DynaBean;
import com.je.common.base.constants.ConstantVars;
import com.je.common.base.db.JEDatabase;
import com.je.common.base.exception.PlatformException;
import com.je.common.base.exception.PlatformExceptionEnum;
import com.je.common.base.service.CommonService;
import com.je.common.base.service.MetaService;
import com.je.common.base.util.ArrayUtils;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.ibatis.extension.plugins.pagination.Page;
import com.je.meta.service.dictionary.MetaDictionaryExtService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据字典拓展功能实现
 *
 * @author wangmm@ketr.com.cn
 * @date 2020/7/31
 */
@Service
public class MetaDictionaryExtServiceImpl implements MetaDictionaryExtService {

    private static final Logger logger = LoggerFactory.getLogger(MetaDictionaryExtServiceImpl.class);
    /**
     * 查询字段使用的字典信息
     */
    private static final String DIC_USED_SQL = "select SY_PRODUCT_ID,JE_CORE_FUNCINFO_ID,SY_PARENT,SY_PRODUCT_CODE,SY_PRODUCT_NAME,FUNCINFO_FUNCNAME,FUNCINFO_FUNCCODE,RESOURCEFIELD_NAME,RESOURCEFIELD_CODE,RESOURCEFIELD_CONFIGINFO from JE_CORE_RESOURCEFIELD INNER JOIN JE_CORE_FUNCINFO on RESOURCEFIELD_FUNCINFO_ID = JE_CORE_FUNCINFO_ID where RESOURCEFIELD_XTYPE in ('cbbfield','treessareafield','treessfield','cbblistfield','multiitem') and RESOURCEFIELD_CONFIGINFO is not null";

    /**
     * 查询所有字典
     */
    private static final String ALL_DIC_SQL = "select * from JE_CORE_DICTIONARY WHERE SY_STATUS = '' OR SY_STATUS = '1'";
    private static final String ALL_DIC_CODE_SQL = "select DICTIONARY_DDCODE,DICTIONARY_BELONGSTONAME from JE_CORE_DICTIONARY WHERE SY_STATUS = '' OR SY_STATUS = '1'";
    /**
     * 基础数据操作封装
     */
    @Autowired
    protected MetaService metaService;
    /**
     * 平台通用业务方法
     */
    @Autowired
    protected CommonService commonService;

    @Override
    public void addLog(String code) {
        //查询被删除的字典
        DynaBean dic = metaService.selectOne("JE_CORE_DICTIONARY", ConditionsWrapper.builder().eq("DICTIONARY_DDCODE", code));
        if (dic == null) {
            logger.error("字典{}在删除日志的时候不存在！", code);
            return;
        }
        //创建日志对象
        DynaBean log = new DynaBean();
        log.table("JE_CORE_DICTIONARY_LOG");
        log.setStr("LOG_DIC_CODE", dic.getStr("DICTIONARY_DDCODE"));
        log.setStr("LOG_DIC_NAME", dic.getStr("DICTIONARY_DDNAME"));
        //字典信息
        log.setStr("LOG_DIC_INFO", JSON.toJSONString(dic.getValues()));
        //查找字典项
        List<Map<String, Object>> items = metaService.selectSql(
                "select * from JE_CORE_DICTIONARYITEM where DICTIONARYITEM_DICTIONARY_ID = {0} order by SY_ORDERINDEX",
                dic.getStr("JE_CORE_DICTIONARY_ID"));
        //字典项信息
        log.setStr("LOG_ITEM_INFO", JSON.toJSONString(items));
        commonService.buildModelCreateInfo(log);
        metaService.insert(log);
    }

    @Override
    public List<Map<String, Object>> selectNotUseDictionary() {
        //查询现有字典CODE
        List<Map<String, Object>> dicList = metaService.selectSql(ALL_DIC_SQL);
        //获取被使用的字典信息
        Set<String> usedCodes = selectFormFieldUsedList().stream().map(p -> p.get("NOFINDDIC_DIC_CODE").toString()).collect(Collectors.toSet());
        //找到没有被使用的字典数据
        return dicList.stream().filter(p -> !usedCodes.contains(p.get("DICTIONARY_DDCODE")))
                .sorted(Comparator.comparing((Map p) -> p.get("DICTIONARY_DDCODE").toString()))
                .collect(Collectors.toList());
    }

    @Override
    public List<Map<String, Object>> selectNotFindDictionary(String sql, Page page) {

        //查询现有字典CODE
        Set<String> codes = metaService.selectSql(ALL_DIC_CODE_SQL).stream().map(p -> p.get("DICTIONARY_DDCODE").toString()).collect(Collectors.toSet());
        List<Map<String, Object>> codeNameList = metaService.selectSql(ALL_DIC_CODE_SQL);
        //获取被使用的信息
        List<Map<String, Object>> usedList = selectFormFieldUsedListBySql(sql);
        //找到字典不存在的字段数据
        List<Map<String, Object>> noUsedList = usedList.stream()
                .filter(p -> !codes.contains(p.get("NOFINDDIC_DIC_CODE")))
                .sorted(Comparator.comparing((Map p) -> p.get("NOFINDDIC_DIC_CODE").toString()).thenComparing((Map p) -> p.get("FUNCINFO_FUNCCODE").toString()))
                .collect(Collectors.toList());
//        for (Map<String, Object> map : noUsedList) {
//            for (Map<String, Object> codeNameMap : codeNameList) {
//                if (codeNameMap.get("DICTIONARY_DDCODE") != null) {
//                    if (codeNameMap.get("DICTIONARY_DDCODE").toString().equals(map.get("NOFINDDIC_DIC_CODE").toString())) {
//                        map.put("NOFINDDIC_SSMK", codeNameMap.get("DICTIONARY_BELONGSTONAME"));
//                        break;
//                    }
//                }
//            }
//        }
        int current = page.getCurrent();
        int size = page.getSize();
        if (size == -1) {
            return noUsedList;
        }
        return startPage(noUsedList, current, size, page);
    }

    /**
     * 开始分页
     *
     * @param list
     * @param pageNum  页码
     * @param pageSize 每页多少条数据
     * @return
     */
    public static List<Map<String, Object>> startPage(List<Map<String, Object>> list, int pageNum, int pageSize, Page page) {
        if (list == null) {
            return null;
        }
        if (list.size() == 0) {
            return list;
        }

        int count = list.size(); // 记录总数
        page.setTotal(count);
        int pageCount = 0; // 页数

        if (count % pageSize == 0) {
            pageCount = count / pageSize;
        } else {
            pageCount = count / pageSize + 1;
        }
        if (pageNum > pageCount) {
            return null;
        }

        int fromIndex = 0; // 开始索引
        int toIndex = 0; // 结束索引

        if (pageNum != pageCount) {
            fromIndex = (pageNum - 1) * pageSize;
            toIndex = fromIndex + pageSize;
        } else {
            fromIndex = (pageNum - 1) * pageSize;
            toIndex = count;
        }
        if (fromIndex > count || toIndex > count) {
            return null;
        }
        page.setRecords(list.subList(fromIndex, toIndex));
        return list.subList(fromIndex, toIndex);
    }

    @Override
    public String generateDictionaryResumeToMeta(String[] idArr) {
        //查询符合条件的字典
        List<DynaBean> dictionaries = metaService.select("JE_CORE_DICTIONARY", ConditionsWrapper.builder().in("JE_CORE_DICTIONARY_ID", Arrays.asList(idArr)));
        if (dictionaries.isEmpty()) {
            logger.error("字典生成概要信息失败，没有找到字典元数据！");
            throw new PlatformException("字典生成概要信息失败，没有找到字典元数据！", PlatformExceptionEnum.UNKOWN_ERROR);
        }
        //遍历字典，生成概要
        String resumeStr = null;
        for (DynaBean d : dictionaries) {
            //字典主键
            String id = d.getStr("JE_CORE_DICTIONARY_ID");
            String type = d.getStr("DICTIONARY_DDTYPE");
            StringBuilder resume = new StringBuilder();
            if ("CUSTOM".equals(type)) {
                resume.append(d.getStr("DICTIONARY_CLASS")).append(",").append(d.getStr("DICTIONARY_METHOD"));
            } else if ("DYNA_TREE".equals(type)) {
                resume.append(d.getStr("DICTIONARY_CLASSNAME"));
            } else if ("SQL".equals(type) || "SQL_TREE".equals(type)) {
                resume.append(d.getStr("DICTIONARY_SQL"));
            } else if ("LIST".equals(type) || "TREE".equals(type)) {
                //查询前50项名称
                List<Map<String, Object>> items = metaService.selectSql(1, 50,
                        "select DICTIONARYITEM_ITEMNAME,SY_PARENT from JE_CORE_DICTIONARYITEM where DICTIONARYITEM_DICTIONARY_ID = {0} and SY_PARENT is not null order by SY_PARENT DESC,SY_ORDERINDEX", id);
                //拼接内容
                String v = items.stream()
                        .filter(item -> StringUtils.isNotBlank(String.valueOf(item.get("SY_PARENT"))))
                        .map(item -> String.valueOf(item.get("DICTIONARYITEM_ITEMNAME")))
                        .collect(Collectors.joining(","));
                resume.append(v);
            }
            if (resume.length() <= 0) {
                throw new PlatformException(String.format("选择字典【%s】，字典项为空！", d.getStr("DICTIONARY_DDCODE")), PlatformExceptionEnum.UNKOWN_ERROR);
            }
            //mysql5 varchar 255 就可以存255个中文
            //Oracle varchar2(byte) 每个汉字占2个字节
            //SQLServer varchar汉字数量 长度/2
            int length = 255;
            //非mysql按照 长度/2 处理
            if (!JEDatabase.getCurrentDatabase().equals(ConstantVars.STR_MYSQL)) {
                length = length / 2;
            }

            if (resume.length() > length) {
                resumeStr = resume.substring(0, length - 1);
            } else {
                resumeStr = resume.toString();
            }
            metaService.executeSql(" update JE_CORE_DICTIONARY set DICTIONARY_ZDXGY = {0} where JE_CORE_DICTIONARY_ID = {1}", resumeStr, id);
        }
        return resumeStr;
    }

    /**
     * 查询被表单字段使用的字典
     * <p>
     * 字段主键：JE_CORE_RESOURCEFIELD_ID
     * 功能名称：FUNCINFO_FUNCNAME,
     * 功能编码：FUNCINFO_FUNCCODE,
     * 字段编码：RESOURCEFIELD_CODE,
     * 字典配置：RESOURCEFIELD_CONFIGINFO
     * 字典编码：DIC_CODE
     *
     * @return 字段使用字典信息
     */
    @Override
    public List<Map<String, Object>> selectFormFieldUsedList() {
        List<Map<String, Object>> list = metaService.selectSql(DIC_USED_SQL);
        List<Map<String, Object>> result = Lists.newArrayList();
        list.forEach(p -> {
            String configInfo = String.valueOf(p.get("RESOURCEFIELD_CONFIGINFO"));
            if (StringUtils.isNotBlank(configInfo)) {
                //被使用的字典编码
                String dicCode = configInfo.split(ArrayUtils.SPLIT)[0];
                p.put("NOFINDDIC_DIC_CODE", dicCode);
                result.add(p);
            }
        });
        return result;
    }


    @Override
    public List<Map<String, Object>> selectFormFieldUsedListBySql(String sql) {
        List<Map<String, Object>> list = null;
        if (Strings.isNullOrEmpty(sql)) {
            list = metaService.selectSql(ConditionsWrapper.builder().apply(DIC_USED_SQL));
        } else {
            list = metaService.selectSql(ConditionsWrapper.builder().apply(DIC_USED_SQL + " and " + sql));
        }
        List<Map<String, Object>> result = Lists.newArrayList();
        list.forEach(p -> {
            String configInfo = String.valueOf(p.get("RESOURCEFIELD_CONFIGINFO"));
            if (StringUtils.isNotBlank(configInfo)) {
                //被使用的字典编码
                String dicCode = configInfo.split(ArrayUtils.SPLIT)[0];
                p.put("NOFINDDIC_DIC_CODE", dicCode);
                result.add(p);
            }
        });
        return result;
    }
}
